/*
 * Copyright 2010 - Jeremie Brebec
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.jb.sharedbills.shared.model.logic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.jb.sharedbills.shared.model.Member;
import org.jb.sharedbills.shared.model.Refund;

/**
 * 
 * @author jeremie
 *
 */
public class RefundBuilder {

	/**
	 * 
	 * @param members
	 * @return
	 */
	public static List<Refund> build( Collection<Member> members ) {
		Set<Member> sortedSetMembers = new TreeSet<Member>( new MemberRefundComparator() );
		sortedSetMembers.addAll( members );
		
		// LinkedList & pollFirst/pollLast not supported on GWT
		List<Member> sortedMembers = new ArrayList<Member>();
		sortedMembers.addAll( sortedSetMembers );
		
		List<Refund> result = new ArrayList<Refund>();
				
		Member from = null;
		Member to = null;
		double fromBalance = 0;
		double toBalance = 0;
		while( !sortedMembers.isEmpty() ) {
			if (from == null) {
				from = sortedMembers.remove(0); // pollFirst
				fromBalance = from.getBalance();
				
				if (fromBalance >= 0)
					break;
			}
			if (to == null) {
				to = sortedMembers.remove( sortedMembers.size() - 1); // pollLast
				toBalance = to.getBalance();
				
				if (toBalance <= 0 )
					break;
			}
			
			Refund f = new Refund();
			result.add( f );
			f.setFrom( from );
			f.setTo( to );
			
			double diff = fromBalance + toBalance;
			if (diff == 0) {
				f.setAmount( toBalance );
				to = null;
				from = null;
			} else if (diff > 0) {
				f.setAmount( - fromBalance );
				toBalance += fromBalance;
				from = null;
			} else if (diff < 0) {
				f.setAmount( toBalance );
				to = null;
				fromBalance += toBalance; 
			}
		}
		return result;
	}
	
	/**
	 * 
	 * @author jeremie
	 *
	 */
	private static class MemberRefundComparator implements Comparator<Member> {

		@Override
		public int compare(Member o1, Member o2) {
			if (o1 == o2) // .equals not necessary
				return 0;
			
			double o1b = o1.getBalance();
			double o2b = o2.getBalance();
			
			if (o1b < o2b)
				return -1;
			else if (o1b > o2b)
				return 1;
			
			return o1.hashCode() - o2.hashCode();
		}
		
	}
}
